package com.ghd.dataviz.handler;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.ghd.dataviz.core.constant.RedisKey;
import com.ghd.dataviz.core.param.UserDisplayPageInfo;
import com.ghd.dataviz.core.security.impl.UserDetailsImpl;
import com.ghd.dataviz.entity.DatavizMessage;
import com.ghd.dataviz.utils.RedisUtil;
import com.ghd.dataviz.utils.UserUtil;
import io.socket.client.Socket;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

/**
 * @author ：ghd
 * @date ：Created in 2020-03-18 21:22
 * @description：
 * @modified By：
 * @version: $
 */
@Slf4j
@Component
public class MessageEventHandler {
    private final SocketIOServer server;
    private final RedisUtil redisUtil;

    private static final String USER_INFO = "USER_INFO";

    public MessageEventHandler(SocketIOServer server, RedisUtil redisUtil) {
        this.server = server;
        this.redisUtil = redisUtil;
    }

    /**
     * 添加connect事件，当客户端发起连接时调用
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        if (client != null) {
            UserDetailsImpl userDetails = UserUtil.getUserInfoByClient(client);
            client.set("USER_INFO", userDetails);
            client.set("USER_NAME", userDetails.getUsername());
            log.info("连接成功 user=" + userDetails.getUsername() +
                    ", sessionId=" + client.getSessionId().toString() );
            // 用户登录的所有客户端
            redisUtil.sSet(userDetails.getUsername(), client.getSessionId().toString());
            // 在线用户数
            redisUtil.incr(RedisKey.COUNT_USER, 1);
            // 添加用户展示页面订阅，用于控制展示页面切换联动
            client.joinRoom(RedisKey.USER_DISPLAY_PAGE + userDetails.getUsername());
        } else {
            log.error("客户端为空");
        }
    }

    /**
     * 添加@OnDisconnect事件，客户端断开连接时调用，刷新客户端信息
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        UserDetailsImpl userDetails = client.get(USER_INFO);
        log.info("客户端断开连接 user=" + userDetails.getUsername() +
                ", sessionId=" + client.getSessionId().toString() );
        // 移除客户端
        redisUtil.setRemove(userDetails.getUsername(), client.getSessionId().toString());
        // 在线用户数
        redisUtil.decr(RedisKey.COUNT_USER, 1);
        client.disconnect();
    }

    /**
     * 消息接收入口
     */
    @OnEvent(value = Socket.EVENT_MESSAGE)
    public void onEvent(SocketIOClient client, AckRequest ackRequest, Object data) {
        log.info("接收到客户端消息:{}", data);
        if (ackRequest.isAckRequested()) {
            // send ack response with data to client
            ackRequest.sendAckData("服务器回答Socket.EVENT_MESSAGE", "好的");
        }
    }

    /**
     * 切换展示页面
     * @param client
     * @param ackRequest
     * @param message);
     */
    @OnEvent(value = "display_page")
    public void showPage(SocketIOClient client, AckRequest ackRequest, DatavizMessage<Integer> message) {
        log.info("接收到客户端消息:{}", message);
        String pageKey = RedisKey.PAGE_PREFIX + message.getData();
        changePage(client, pageKey);
        // 缓存用户展示的页面信息，用于新客户端登录的初始化
        redisUtil.hset(RedisKey.USER_DISPLAY_INFO, client.get("USER_NAME"),
                UserDisplayPageInfo.builder().pageId(message.getData().longValue()).build());
        // 通知所有当前用户登录的客户端订阅新页面，联动效果
        server.getRoomOperations(
                RedisKey.USER_DISPLAY_PAGE + client.get("USER_NAME"))
                .sendEvent("change_page", client, DatavizMessage.create("change_page", message.getData()));
    }

    /**
     * 切换展示页面
     * @param client
     * @param ackRequest
     * @param message
     */
    @OnEvent(value = "change_page")
    public void changePage(SocketIOClient client, AckRequest ackRequest, DatavizMessage<Integer> message) {
        String pageKey = RedisKey.PAGE_PREFIX + message.getData();
        changePage(client, pageKey);
    }

    /**
     * 切换页面
     * @param client
     * @param newPageKey
     */
    private void changePage(SocketIOClient client, String newPageKey) {
        // 取消之前订阅的页面
        client.getAllRooms().stream()
                .filter(name -> StringUtils.startsWith(name, RedisKey.PAGE_PREFIX))
                .forEach(client::leaveRoom);
        // 添加新页面数据订阅
        client.joinRoom(newPageKey);
    }
}
